/*
	DS Kanji Flash
	Copyright (C) 2009,2010 Bernhard Schelling

	This file is part of DS Kanji Flash.

	DS Kanji Flash is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	DS Kanji Flash is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with DS Kanji Flash.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "global.h"
#include "../build/kanas_oft.h"
#include "../build/alphas_oft.h"
#include "../build/skanji.h"
#include "../build/metadata.h"

cScene* MapScenes[SCN_NUMOFSCENES];
cScene* pSceneCurrent = NULL;

UL_FONT _fntJapanese = { NULL, NULL, NULL, 1, 15, 15, 0, 0 };
UL_FONT *fntJapanese = &_fntJapanese; //(UL_FONT*)0xDEADBEEF;
UL_FONT *fntKanas = NULL;
UL_FONT *fntAlphas = NULL;

bool bDrawFontShadows = true;

struct sMapKanjiUCS
{
	sMapKanjiUCS() : eFirst(NULL), eLast(NULL) { }
	struct sEntry { wchar_t ucs; u16 index; sEntry *next; } *eFirst, *eLast;
	void Add(wchar_t ucs, u16 index) { sEntry *ePrev = eLast; eLast = new sEntry(); eLast->ucs = ucs; eLast->index = index; if (ePrev) ePrev->next = eLast; if (!eFirst) eFirst = eLast; }
	int Get(wchar_t ucs)  { for (sEntry *p = eFirst; p; p = p->next) if (p->ucs == ucs) return p->index; return -1; }
} mapKanjiUCS;

#define IMGSKANJINUMBUF 25
UL_IMAGE* imgSKanji[IMGSKANJINUMBUF];
u16 imgSKanjiLoaded[IMGSKANJINUMBUF];
u8 imgSKanjiIndex = 0;
int DrawSKanji(int iKanji, int x, int y, UL_COLOR col, bool draw)
{
	int w = skanji_width[iKanji];
	if (!draw) return w+4;

	UL_IMAGE* img = NULL;
	int iSubKanji = iKanji%SKANJI_PERPIC;
	int iToLoad = (iKanji - iSubKanji) / SKANJI_PERPIC;
	for (int i = 0; i < IMGSKANJINUMBUF; i++) if (!imgSKanji[i]) break; else if (imgSKanjiLoaded[i] == iToLoad) { img = imgSKanji[i]; break; }
	if (!img)
	{
		if (imgSKanjiIndex == IMGSKANJINUMBUF) imgSKanjiIndex = 0;
		if (imgSKanji[imgSKanjiIndex]) ulDeleteImage(imgSKanji[imgSKanjiIndex]);
		imgSKanjiLoaded[imgSKanjiIndex] = iToLoad;
		ul_firstPaletteColorOpaque = 1;
		img = imgSKanji[imgSKanjiIndex++] = ulLoadImageFilePNG((char*)skanji_png[iToLoad], (int)skanji_png[iToLoad+1]-(int)skanji_png[iToLoad], UL_IN_VRAM, UL_PF_PAL2);
		ul_firstPaletteColorOpaque = 0;
	}

	int u = (iSubKanji%SKANJI_PERWIDTH)*SKANJI_WIDTH, v = (iSubKanji/SKANJI_PERWIDTH)*SKANJI_HEIGHT;

	ulSetTexture(img);
	if (bDrawFontShadows)
	{
		GFX_BEGIN = GL_QUADS;
		GFX_COLOR = COL_BLACK;
		for (u8 c = 0; c < 9; c++) if (c!=4) { ulVertexUVXY(u, v, x-1+(c%3), y-1+(c/3)); ulVertexUVXY(u, v+SKANJI_HEIGHT, x-1+(c%3), y-1+(c/3)+SKANJI_HEIGHT); ulVertexUVXY(u+w, v+SKANJI_HEIGHT, x-1+(c%3)+w, y-1+(c/3)+SKANJI_HEIGHT); ulVertexUVXY(u+w, v, x-1+(c%3)+w, y-1+(c/3)); }
		GFX_END = 0;
		ul_currentDepth += ul_autoDepth;
	}
	GFX_BEGIN = GL_QUADS;
	GFX_COLOR = col;
	ulVertexUVXY(u, v, x, y);
	ulVertexUVXY(u, v+SKANJI_HEIGHT, x, y+SKANJI_HEIGHT);
	ulVertexUVXY(u+w, v+SKANJI_HEIGHT, x+w, y+SKANJI_HEIGHT);
	ulVertexUVXY(u+w, v, x+w, y);
	GFX_END = 0;

	ul_currentDepth += ul_autoDepth;
	return w+4;
}

void InitTextDisplay()
{
	memset(imgSKanji, NULL, sizeof(imgSKanji));
	//for (int i = SKANJIOFTS_NUM-1; i >= 0; i--)
	//{
	//	VIRTUAL_FILE *vfFontKanji = VirtualFileOpen((void*)skanji_oft[i], (int)skanji_oft[i+1]-(int)skanji_oft[i], VF_MEMORY, VF_O_READ);
	//	fntSKanji[i] = ulLoadFont(vfFontKanji);
	//	VirtualFileClose(vfFontKanji);
	//}
	VIRTUAL_FILE *vfFontKanas = VirtualFileOpen((void*)kanas_oft, (int)kanas_oft_size, VF_MEMORY, VF_O_READ);
	fntKanas = ulLoadFont(vfFontKanas);
	VirtualFileClose(vfFontKanas);
	VIRTUAL_FILE *vfFontAlphas = VirtualFileOpen((void*)alphas_oft, (int)alphas_oft_size, VF_MEMORY, VF_O_READ);
	fntAlphas = ulLoadFont(vfFontAlphas);
	VirtualFileClose(vfFontAlphas);
	//imgSKanji = ulLoadImageFilePNG((char*)skanjiofts_png, (int)skanjiofts_png_size, UL_IN_RAM, UL_PF_PAL2);
	for (u16 i=0; i < kanji_num; i++) mapKanjiUCS.Add(GetUCS(kanji[i]), i);
}

int sRect::x2() { return x+w; }
int sRect::y2() { return y+h; }
bool sRect::inside(int tx, int ty) { return !(tx<x||tx>x+w||ty<y||ty>y+h); }
bool sRect::touchButton() { return !(ul_keys.touch.x<x-5||ul_keys.touch.x>x+w+6||ul_keys.touch.y<y-8||ul_keys.touch.y>y+h+4); }
sRect::sRect(int x,int y,int w,int h) : x(x), y(y), w(w), h(h) { }

cScene::cScene(int iType) : WantsKeyHeldEvents(false), WantsKeyReleaseEvents(false), WantsEveryFrame(false)
{
	MapScenes[iType] = this;
}

void cScene::GoTo(int Type, void *param1, void *param2, void *param3)
{
	cScene* pSceneNew = MapScenes[Type];
	if (!pSceneNew || pSceneCurrent == pSceneNew) return;
	pSceneCurrent = pSceneNew;
	pSceneCurrent->Enter(param1, param2, param3);
}

cScene* cScene::Get(int Type)
{
	return MapScenes[Type];
}

int SetUCS(char *str, wchar_t code)
{
	if (code >= 0x800) { *str++ = char(0xE0 + ((code >> 12) & 0xF)); *str++ = char(0x80 + ((code >> 6) & 0x3F)); *str = char(0x80 + ((code) & 0x3F)); return 3; }
	if (code >= 0x80)  { *str++ = char(0xC0 + ((code >> 6) & 0x1F)); *str = char(0x80 + ((code) & 0x3F)); return 2; }
	*str = char(code); return 1;
}

wchar_t GetUCS(const char **txt)
{
  if      (*txt[0] > 0xc1 && *txt[0] < 0xe0) { *txt += 2; return (*((*txt)-2)-192)*64 + (*((*txt)-1)-128); }
	else if (*txt[0] > 0xdf && *txt[0] < 0xf0) { *txt += 3; return (*((*txt)-3)-224)*4096 + (*((*txt)-2)-128)*64 + (*((*txt)-1)-128); }
	else if (*txt[0] > 0xef) return 0;
  else return *((*txt)++);
}

wchar_t GetUCS(const char *txt)
{
  if      (txt[0] > 0xc1 && txt[0] < 0xe0) return ((txt[0]-192)*64) + (txt[1]-128);
	else if (txt[0] > 0xdf && txt[0] < 0xf0) return (txt[0]-224)*4096 + (txt[1]-128)*64 + (txt[2]-128);
	else if (txt[0] > 0xef) return 0;
  else return txt[0];
}

wchar_t GetKanjiUCS(int iKanji)
{
	return GetUCS(kanji[iKanji]);
}

int GetKanjiIndex(wchar_t numChar)
{
	return mapKanjiUCS.Get(numChar);
}

int StringGetWidthOrDraw(UL_FONT* fnt, const char *str, int numchars, bool draw, bool measure, int x, int y, UL_COLOR col)
{
	int orgx = x;
	const char *end = (numchars == -1 ? (char*)0xFFFFFFFF : str+numchars);
	if (fnt == fntJapanese)
	{
		while (*str!=0&&*str!='\n'&&str<end)
		{
			char cShow;
			wchar_t numChar;
			int iKanji;
			numChar = GetUCS(&str);
			if (numChar < 128) { ulSetFont(fntAlphas); cShow = (char)numChar; }
			else if (numChar >= 0x3000 && numChar <= 0x30FF) { ulSetFont(fntKanas); cShow = (char)(numChar-0x3000); }
			//else if ((iKanji = mapKanjiUCS.Get(numChar)) != -1) { ulSetFont(fntSKanji[iKanji/255]); cShow = 1+(iKanji%255); }
			else if ((iKanji = mapKanjiUCS.Get(numChar)) != -1) { x += DrawSKanji(iKanji, x, y, col, draw); continue; }
			else return x;
			if (draw)
			{
				if (bDrawFontShadows) { ulSetTextColor(RGB15(0, 0, 0)); for (u8 c = 0; c < 9; c++) if (c!=4) ulDrawChar(x-1+(c%3), y-1+(c/3), cShow); }
				//if (bDrawFontShadows) { ulSetTextColor(RGB15(0, 0, 0)); for (u8 c = 0; c < 4; c++) ulDrawString(x+(c==0?-1:(c==1?1:0)), y+(c==2?-1:(c==3?1:0)), cBuffer); }
				ulSetTextColor(col);
				//ulDrawString(x, y, cBuffer);
				ulDrawChar(x, y, cShow);
			}
			x += ul_curFont->charWidths[(int)cShow];
		}
	}
	else
	{
		ulSetFont(fnt);
		char cBuffer[16]; unsigned char cbSize = 0;
		for (;*str!=0&&*str!='\n'&&str<end;str++)
		{
			if (fnt == fntKanas)
			{
				wchar_t numChar = *str;
				if (numChar > 127) { numChar = GetUCS(&str); str--; }
				if (numChar == 0xFF5E || numChar == 0x7E) numChar = 0x301C;
				cBuffer[cbSize++] = (char)(numChar - 0x3000);
			}
			else cBuffer[cbSize++] = *str;
			if (cbSize == 15 || (*(str+1)==0||*(str+1)=='\n'||(str+1)==end))
			{
				cBuffer[cbSize] = 0; cbSize = 0;
				if (draw)
				{
					ulSetTextColor(RGB15(0, 0, 0));
					if (bDrawFontShadows) for (u8 c = 0; c < 9; c++) if (c!=4) ulDrawString(x-1+(c%3), y-1+(c/3), cBuffer);
					//if (bDrawFontShadows) for (u8 c = 0; c < 4; c++) ulDrawString(x+(c==0?-1:(c==1?1:0)), y+(c==2?-1:(c==3?1:0)), cBuffer);
					ulSetTextColor(col);
					ulDrawString(x, y, cBuffer);
				}
				if (measure || (*(str+1)!=0&&*(str+1)!='\n'&&(str+1)!=end)) x += ulGetStringWidth(cBuffer);
			}
		}
	}
	return x-orgx;
}

void drawButtonBG(int x, int y, int w, int h, u8 mode, const char* str, UL_COLOR col)
{
	UL_COLOR colBkT, colBkB, colUpT, colUpB, colDnT, colDnB;
	switch (mode)
	{
		case BUTTON_BG_RED:
			colBkT = RGB15( 8,  0,  0); colBkB = RGB15(31,  9,  9);
			colUpT = RGB15(31, 25, 25); colUpB = RGB15(20, 12, 12);
			colDnT = RGB15(22,  5,  5); colDnB = RGB15(31, 17, 17);
			break;
		case BUTTON_BG_GREEN:
			colBkT = RGB15( 0,  8,  0); colBkB = RGB15( 9, 31,  9);
			colUpT = RGB15(25, 31, 25); colUpB = RGB15(12, 20, 12);
			colDnT = RGB15( 5, 22,  5); colDnB = RGB15(17, 31, 17);
			break;
		case BUTTON_BG_BLUE:
			colBkT = RGB15( 0,  0,  8); colBkB = RGB15( 9,  9, 31);
			colUpT = RGB15(25, 25, 31); colUpB = RGB15(12, 12, 20);
			colDnT = RGB15( 5,  5, 22); colDnB = RGB15(17, 17, 31);
			break;
		case BUTTON_BG_WHITE:
		default:
			colBkT = RGB15( 2,  2,  2); colBkB = RGB15( 8,  8,  8);
			colUpT = RGB15(31, 31, 31); colUpB = RGB15(20, 20, 20);
			colDnT = RGB15(22, 22, 22); colDnB = RGB15(31, 31, 31);
			break;
	}
	ulDrawGradientRect(x-5, y-8, x+w+6, y+h+6, colBkT, colBkT, colBkB, colBkB);
	ulDrawGradientRect(x-3, y-6, x+w+4, y - 2, colUpT, colUpT, colUpB, colUpB);
	ulDrawGradientRect(x-3, y-2, x+w+4, y+h+4, colDnT, colDnT, colDnB, colDnB);
	
	if (str) displayStringCenter(ul_lcdFont, str, x, y + (h-ul_lcdFont->charHeight)/2, w, col);
}

void drawButtonBG(sRect &rec, u8 mode, const char* str, UL_COLOR col)
{
	drawButtonBG(rec.x, rec.y, rec.w, rec.h, mode, str, col);
}

/*
#include <sstream>
string itos(int x)
{
	ostringstream o;
	if (!(o << x)) return string();
	return o.str();
}
*/

/*
void EraseUCS(string &str, int at)
{
	if (at == -1) at = str.length()-1;
	if (at < 0 || at >= (int)str.length()) return;
	if (at && str[at] >= 0x80 && str[at] < 0xC0) at--;
	if (at && str[at] >= 0x80 && str[at] < 0xC0) at--;
	if (str[at] >= 0xE0) str.erase(at, 3);
	else if (str[at] >= 0xC0) str.erase(at, 2);
	else if (str[at] < 0x80) str.erase(at, 1);
}

void PutUCS(string &str, int code)
{
	if (code >= 0x800) { str += char(0xE0 + ((code >> 12) & 0xF)); str += char(0x80 + ((code >> 6) & 0x3F)); str += char(0x80 + ((code) & 0x3F)); return; }
	if (code >= 0x80) {str += char(0xC0 + ((code >> 6) & 0x1F)); str += char(0x80 + ((code) & 0x3F)); return; }
	str += char(code); 
}

//unsigned char cKanaKeyboard[] = {
//// a     ka    ga    sa    za    ta    da    na    ha    ba    pa    ma    ra    ya   wa/o/n
//	0x42, 0x4B, 0x4C, 0x55, 0x56, 0x5F, 0x60, 0x6A, 0x6F, 0x70, 0x71, 0x7E, 0x89, 0x8F, 0x93 ,
//	0x44, 0x4D, 0x4E, 0x57, 0x58, 0x61, 0x62, 0x6B, 0x72, 0x73, 0x74, 0x7F, 0x8A, 0x92, 0x0C , 
//	0x46, 0x4F, 0x50, 0x59, 0x5A, 0x64, 0x65, 0x6C, 0x75, 0x76, 0x77, 0x80, 0x8B, 0x84, 0x0D , 
//	0x48, 0x51, 0x52, 0x5B, 0x5C, 0x66, 0x67, 0x6D, 0x78, 0x79, 0x7A, 0x81, 0x8C, 0x86, 0x7E , 
//	0x4A, 0x53, 0x54, 0x5D, 0x5E, 0x68, 0x69, 0x6E, 0x7B, 0x7C, 0x7D, 0x82, 0x8D, 0x88, 0x02 , 
//};

unsigned char cKanaKeyboard[] = {
//           a    ka    sa    ta    na    ha    ma    ra    ya   sya  wa/o/n 
	0xFB, 0, 0x42, 0x4B, 0x55, 0x5F, 0x6A, 0x6F, 0x7E, 0x89, 0x84, 0x83, 0x8F, 0, 0    ,
	0x9B, 0, 0x44, 0x4D, 0x57, 0x61, 0x6B, 0x72, 0x7F, 0x8A, 0   , 0   , 0x92, 0, 0x0C , 
	0x9C, 0, 0x46, 0x4F, 0x59, 0x64, 0x6C, 0x75, 0x80, 0x8B, 0x86, 0x85, 0   , 0, 0x0D , 
	0   , 0, 0x48, 0x51, 0x5B, 0x66, 0x6D, 0x78, 0x81, 0x8C, 0   , 0   , 0   , 0, 0x1C , 
	0   , 0, 0x4A, 0x53, 0x5D, 0x68, 0x6E, 0x7B, 0x82, 0x8D, 0x88, 0x87, 0x93, 0, 0x02 , 
};

unsigned char cAsciiKeyboardUpper[] = {
	'1','2','3','4','5','6','7','8','9','0' , '-' , '='  ,
	'Q','W','E','R','T','Y','U','I','O','P' , '[' , ']'  ,
	'A','S','D','F','G','H','J','K','L',';' , '~' , '\\' ,
	'Z','X','C','V','B','N','M',',','.','/' , '(' , ')'  ,
};

unsigned char cAsciiKeyboardLower[] = {
	'1','2','3','4','5','6','7','8','9','0' , '-' , '='  ,
	'q','w','e','r','t','y','u','i','o','p' , '[' , ']'  ,
	'a','s','d','f','g','h','j','k','l',';' , '~' , '\\' ,
	'z','x','c','v','b','n','m',',','.','/' , '(' , ')'  ,
};

unsigned char *cAsciiKeyboard = cAsciiKeyboardLower;
bool bKanaKatakana = false;
char cKanaDakutenMode = 0;
#define ASCII_SHIFTED (cAsciiKeyboard == cAsciiKeyboardUpper)
unsigned char XOFF[] = { 16, 21, 26, 31 };
#define KBX 9
#define KBY 79

unsigned char KanaDakutenMod(unsigned char c)
{
	if (!cKanaDakutenMode || c == 0xFB || c == 0x9B || c == 0x9C) return c;
	if (cKanaDakutenMode == 1 && c >= 0x4B && c <= 0x69) return c+1;
	if (c >= 0x6F && c <= 0x7D) return c+cKanaDakutenMode;
	return 0;
}

void ProcessKanaKeyboardInput(string &strWord)
{
	if (!ul_keys.pressed.touch || ul_keys.touch.x < KBX || ul_keys.touch.x > 256-KBX || ul_keys.touch.y < KBY) return;
	int ky = (ul_keys.touch.y - KBY)/19;
	int kx = (ul_keys.touch.x - KBX)/16;
	if      (ky >= 5 && kx <= 3) bKanaKatakana ^= 1;
	else if (ky >= 5 && kx <= 8) strWord += ' '; //space
	else if (ky >= 5 && kx  > 8) EraseUCS(strWord); //backspace
	else if (kx == 0 && ky  < 4) cKanaDakutenMode = ky;
	else if (KanaDakutenMod(cKanaKeyboard[ky*15+kx])) PutUCS(strWord, 0x3000+(bKanaKatakana?0x60:0)+KanaDakutenMod(cKanaKeyboard[ky*15+kx]));
}

void ProcessAsciiKeyboardInput(string &strWord)
{
	if (!ul_keys.pressed.touch ||ul_keys.touch.x < KBX+XOFF[0] || ul_keys.touch.x > 256-KBX-XOFF[0] || ul_keys.touch.y < KBY+19) return;
	int ky = (ul_keys.touch.y - KBY)/19-1;
	int kx = (ul_keys.touch.x - KBX - XOFF[(ky==4?0:ky)])/16;
	if      (ky <  4 && (ul_keys.touch.x<KBX+XOFF[(ky==4?0:ky)]||kx>=12)) { }
	else if (ky >= 4 && kx <= 3) cAsciiKeyboard = (ASCII_SHIFTED ? cAsciiKeyboardLower : cAsciiKeyboardUpper);
	else if (ky >= 4 && kx <= 7) strWord += ' '; //space
	else if (ky >= 4 && kx  > 7) EraseUCS(strWord); //backspace
	else if (cAsciiKeyboard[ky*12+kx]) strWord += cAsciiKeyboard[ky*12+kx];
}

void DrawKanaKeyboard()
{
	ulDrawGradientRect(KBX-2, KBY-2, KBX+15*16, KBY+5*19+15, RGB15(22, 22, 22), RGB15(22, 22, 22), RGB15(28, 28, 28), RGB15(28, 28, 28));
	for (int y = 0; y < 5; y++) for (int x = 0; x < 15; x++) ulDrawGradientRect(KBX+x*16, KBY+y*19, KBX+14+x*16, KBY+17+y*19, RGB15(5, 5, 5), RGB15(5, 5, 5), RGB15(15, 15, 15), RGB15(15, 15, 15));
	ulSetFont(fntKanas);

	ulSetTextColor(0);
	for (u8 c = 0; c < 9; c++) if (c!=4) for (int y = 0; y < 5; y++) for (int x = 0; x < 15; x++) if (KanaDakutenMod(cKanaKeyboard[y*15+x]))
		ulDrawChar(1+KBX+x*16-1+(c%3), KBY+1+y*19-1+(c/3), (bKanaKatakana?0x60:0)+KanaDakutenMod(cKanaKeyboard[y*15+x]));

	ulSetTextColor(COL_WHITE);
	for (int y = 0; y < 5; y++) for (int x = 0; x < 15; x++) if (KanaDakutenMod(cKanaKeyboard[y*15+x]))
		ulDrawChar(1+KBX+x*16, KBY+1+y*19, (bKanaKatakana?0x60:0)+KanaDakutenMod(cKanaKeyboard[y*15+x]));

	ulDrawGradientRect(KBX+0*16, KBY+5*19, KBX+14+3*16,  KBY+13+5*19, RGB15(5, 5, 5), RGB15(5, 5, 5), RGB15(15, 15, 15), RGB15(15, 15, 15));
	ulDrawGradientRect(KBX+4*16, KBY+5*19, KBX+14+8*16,  KBY+13+5*19, RGB15(5, 5, 5), RGB15(5, 5, 5), RGB15(15, 15, 15), RGB15(15, 15, 15));
	ulDrawGradientRect(KBX+9*16, KBY+5*19, KBX+14+14*16, KBY+13+5*19, RGB15(5, 5, 5), RGB15(5, 5, 5), RGB15(15, 15, 15), RGB15(15, 15, 15));
	displayStringCenter(ul_lcdFont, "MODE",      KBX+0*16, 3+KBY+5*19, 14+3*16, COL_WHITE);
	displayStringCenter(ul_lcdFont, "SPACE",     KBX+4*16, 3+KBY+5*19, 14+4*16, COL_WHITE);
	displayStringCenter(ul_lcdFont, "BACKSPACE", KBX+9*16, 3+KBY+5*19, 14+5*16, COL_WHITE);
}

void DrawAsciiKeyboard()
{
	ulDrawGradientRect(KBX+XOFF[0]-2, KBY-2+19, KBX+XOFF[3]+12*16, KBY+5*19+15, RGB15(22, 22, 22), RGB15(22, 22, 22), RGB15(28, 28, 28), RGB15(28, 28, 28));
	for (int y = 0; y < 4; y++) for (int x = 0; x < 12; x++) ulDrawGradientRect(KBX+XOFF[y]+x*16, 19+KBY+y*19, KBX+XOFF[y]+14+x*16, 19+KBY+17+y*19, RGB15(5, 5, 5), RGB15(5, 5, 5), RGB15(15, 15, 15), RGB15(15, 15, 15));
	ulSetFont(ul_lcdFont);
	
	ulSetTextColor(0);
	for (u8 c = 0; c < 9; c++) if (c!=4) for (int y = 0; y < 4; y++) for (int x = 0; x < 12; x++) if (cAsciiKeyboard[y*12+x])
		ulDrawChar(1+KBX+XOFF[y]+x*16-1+(c%3), 19+KBY+1+y*19-1+(c/3), cAsciiKeyboard[y*12+x]);			

	ulSetTextColor(COL_WHITE);
	for (int y = 0; y < 4; y++) for (int x = 0; x < 12; x++) if (cAsciiKeyboard[y*12+x])
		ulDrawChar(1+KBX+XOFF[y]+x*16, 19+KBY+1+y*19, cAsciiKeyboard[y*12+x]);			

	ulDrawGradientRect(KBX+XOFF[0]+0*16, KBY+5*19, KBX+XOFF[0]+14+3*16, KBY+13+5*19, RGB15(5, 5, 5), RGB15(5, 5, 5), RGB15(15, 15, 15), RGB15(15, 15, 15));
	ulDrawGradientRect(KBX+XOFF[0]+4*16, KBY+5*19, KBX+XOFF[0]+14+7*16, KBY+13+5*19, RGB15(5, 5, 5), RGB15(5, 5, 5), RGB15(15, 15, 15), RGB15(15, 15, 15));
	ulDrawGradientRect(KBX+XOFF[0]+8*16, KBY+5*19, KBX+XOFF[3]+14+11*16, KBY+13+5*19, RGB15(5, 5, 5), RGB15(5, 5, 5), RGB15(15, 15, 15), RGB15(15, 15, 15));
	displayStringCenter(ul_lcdFont, "SHIFT",     KBX+XOFF[0]+0*16, 3+KBY+5*19, 14+3*16, COL_WHITE);
	displayStringCenter(ul_lcdFont, "SPACE",     KBX+XOFF[0]+4*16, 3+KBY+5*19, 14+3*16, COL_WHITE);
	displayStringCenter(ul_lcdFont, "BACKSPACE", KBX+XOFF[0]+8*16, 3+KBY+5*19, XOFF[3]-XOFF[0]+14+3*16, COL_WHITE);
}
*/

void glDrawTriangle(int x0, int y0, int x1, int y1, int x2, int y2, UL_COLOR color)
{
	ulDisableTexture();
	ulVertexBegin(GL_TRIANGLES);
	ulVertexColor(color);
	ulVertexXY(x0, y0);
	ulVertexXY(x1, y1);
	ulVertexXY(x2, y2);
	ulVertexEnd();
	ulVertexHandleDepth();
}

void glDrawCircle(int x, int y, int rad, UL_COLOR colColor)
{
	ulDisableTexture();
	ulVertexBegin(GL_TRIANGLE_STRIP);
	ulVertexColor(colColor);
	for (int a = 0; a <= 512; a += 64)
	{
		ulVertexXY(x, y);
		ulVertexXY(x + ulSin(a, rad/2-1), y + ulCos(a, rad/2-1));
		ulVertexXY(x + ulSin((a+32), rad/2-1), y + ulCos((a+32), rad/2-1));
	}
	ulVertexEnd();
	ulVertexHandleDepth();
}

float sqrt(float number)
{
	float x = number * 0.5F;
	float y  = number;
	long i  = *(long*)(void*)&y;
	i  = 0x5f3759df - ( i >> 1 );
	y  = *(float*)(void*)&i;
	y  = y * ( 1.5F - ( x * y * y ) );
	y  = y * ( 1.5F - ( x * y * y ) );
	return number * y;
}

u16 ulAtan2(s16 y, s16 x)
{
	const u16 coeff_1 = UL_PI / 4;
	const u16 coeff_2 = 3 * coeff_1;
	u16 abs_y = ulAbs(y);
	u16 angle;
	if (x >= 0) angle = coeff_1 - coeff_1 * ((x - abs_y) / (float)(x + abs_y)) + 0.999999f;
	else        angle = coeff_2 - coeff_1 * ((x + abs_y) / (float)(abs_y - x)) + 0.999999f;
	return y < 0 ? UL_PI*2-angle : angle;
}

void glDrawLine(u16 x1, u16 y1, u16 x2, u16 y2, u16 tStart, u16 tEnd, UL_COLOR colStart, UL_COLOR colEnd, bool bFeather)
{
  u16 angle = ulAtan2(y2 - y1, x2 - x1);
	s16 tsin = ul_sin[angle], tcos = ul_cos[angle];
	ulDisableTexture();
  float tsina1 = tStart * tsin / 512.0f;
  float tcosa1 = tStart * tcos / 512.0f;
  float tsina2 = tEnd * tsin / 512.0f;
  float tcosa2 = tEnd * tcos / 512.0f;
	ulVertexBegin(GL_TRIANGLE_STRIP);
	ulVertexColor(colEnd);
	if (bFeather)
	{ 
		ulVertexXY(x2 + tsina2 + tcosa2/2, y2 - tcosa2 + tsina2/2);
		ulVertexXY(x2 - tsina2 + tcosa2/2, y2 + tcosa2 + tsina2/2);
		ulVertexColor(colStart);
		ulVertexXY(x1 + tsina1 - tcosa2/2, y1 - tcosa1 - tsina2/2);
		ulVertexXY(x1 - tsina1 - tcosa2/2, y1 + tcosa1 - tsina2/2);
	}
	else
	{
		ulVertexXY(x2 + tsina2, y2 - tcosa2);
		ulVertexXY(x2 - tsina2, y2 + tcosa2);
		ulVertexColor(colStart);
		ulVertexXY(x1 + tsina1, y1 - tcosa1);
		ulVertexXY(x1 - tsina1, y1 + tcosa1);
	}
	ulVertexEnd();
	ulVertexHandleDepth();
}

/*
#define eof(vf) (vf->type == VF_MEMORY ? !VirtualFileEof(vf) : VirtualFileEof(vf))
char *FixedFileGets(char *str, int maxLen, VIRTUAL_FILE *f)
{
	if (f->type != VF_MEMORY) return VirtualFileGets(str, maxLen, f);
	const int blockSize = 16;
	int offset = 0, i, size;
	while(1)
	{
		size = VirtualFileRead(str + offset, 1, ulMin(maxLen - 1 - offset, blockSize), f);
		if (offset + size < maxLen) str[offset + size] = 0;
		for (i=offset;i<offset+blockSize;i++)
		{
			if (str[i] == '\n') { str[i+1] = 0; VirtualFileSeek(f, -size + (i - offset) + 1, SEEK_CUR); }
			if (str[i] == '\n' || str[i] == 0) return str;
		}
		offset += blockSize;
	}
	return str;
}
*/

/*
#include <zlib.h>
	#define VFZCHUNK 16384
	z_stream *vfzstrm;
	char *vfzin, *vfzout;
	bool VFZStreamInit()
	{
	vfzstrm = (z_stream*)malloc(sizeof(z_stream));
	vfzin = (char*)malloc(VFZCHUNK);
	vfzout = (char*)malloc(VFZCHUNK);
	memset(vfzstrm, 0, sizeof(z_stream));
	memset(vfzout, 0, VFZCHUNK);
	return (inflateInit(vfzstrm) == Z_OK);
	}
	
		void VFZStreamDeInit()
		{
		if (vfzstrm) { inflateEnd(vfzstrm); free(vfzstrm); vfzstrm = NULL; }
		if (vfzin && vfzout) { free(vfzin); free(vfzout); vfzin = vfzout = NULL; }
		}
		
			char* VFZStreamGets(char *str, int maxLen, VIRTUAL_FILE *f)
			{
			if (!vfzstrm) { str[0] = 0; return str; }
			char *o = str;
			for (char *c = vfzout;*c && c<vfzout+VFZCHUNK;*(o++)=*c, c++)
			if (*c=='\n'||o-str>=maxLen) { *(o++) = 0; c++; memmove(vfzout, c, vfzout+VFZCHUNK-c); *(vfzout+VFZCHUNK-c+vfzout)=0; return str; }
			while (1)
			{
			if (vfzstrm->avail_in) do
			{
			vfzstrm->avail_out = VFZCHUNK;
			vfzstrm->next_out = (unsigned char*)vfzout;
			if (inflate(vfzstrm, Z_NO_FLUSH) != Z_OK) { VFZStreamDeInit(); *o = 0; return str; }
			for (char *c = vfzout;*c && c<vfzout+VFZCHUNK-vfzstrm->avail_out;*(o++)=*c, c++)
			if (*c == '\n'||o-str>=maxLen) { *(o++) = 0; c++; memmove(vfzout, c, vfzout+VFZCHUNK-c); *(vfzout+VFZCHUNK-c+vfzout)=0; return str; }
			}	while (vfzstrm->avail_out == 0);
			if ((vfzstrm->avail_in = VirtualFileRead(vfzin, 1, VFZCHUNK, f)) == 0) break;
			vfzstrm->next_in = (unsigned char*)vfzin;
			//o += sprintf(o, "S:%dI:%d", metadata_txt_z_size, vfzstrm->avail_in);
			}
			*o = 0; return str;
}
*/
